module PIECoder(
	input			Clk		,
	input			Rst		,
	input	[7:0]	Din		,
	input			Le		,
	input	[3:0]	N		,
	output	reg		Dout	
);

	parameter		IDLE = 2'b00	;
	parameter		SOF = 2'b01		;
	parameter		BYTE = 2'b10	;
	parameter		EOF = 2'b11		;
	
	reg		[1:0]	current_state	;
	
	reg		[3:0]	n_reg			; //reg to store the N
	reg		[7:0]	din_reg[15:0]	; //mem16*8 to store the input data
	reg		[3:0]	din_cnt			; //count the store Din number
	reg				din_cnt_en		; //enable the din_cnt
	
	reg		[4:0]	pie_clk_cnt		; //count and divide the Clk by 5
	reg				pie_clk			; //the PIE used CLK
	
	reg		[1:0]	bit_cnt			;
	reg				bit_en			;
	reg		[1:0]	bit_cnt_full	;
	wire			bit_cnt_up		;
	reg		[2:0]	byte_cnt		;
	reg		[2:0]	byte_cnt_full	;
	wire			byte_cnt_up		;
	reg		[3:0]	frame_cnt		;
	reg				frame_en		;
	wire			frame_cnt_up	;
	
	integer			index			;
	reg				current_data	;
	
	
	always @(posedge Clk or negedge Rst) begin
		//Reset
		if(!Rst) begin
			current_state 		<=		IDLE	;
			n_reg 				<=		0		;		
			din_cnt_en			<= 		0		;
			din_cnt 			<= 		1		;
			pie_clk_cnt 		<= 		0		;
			pie_clk				<= 		0		;
			for(index=0; index<16; index=index+1) begin
				din_reg[index] 	<= 		0		;
			end
			bit_cnt				<=		0		;
			byte_cnt	        <=      0		;
			frame_cnt	        <=      0		;
		end
		else begin
			//pie_clk
			if(pie_clk_cnt == 4) begin
				pie_clk_cnt <= 0;
				pie_clk <= 1;
			end
			else begin
				pie_clk_cnt <= pie_clk_cnt + 1;
				pie_clk <= 0;
			end
			//FSM
			case(current_state)
				IDLE: begin
					if(Le) current_state <= SOF;
					else current_state <= current_state;
				end 
				SOF: begin
					if(byte_cnt_up) current_state <= BYTE;
					else current_state <= current_state;
				end
				BYTE: begin
					if(frame_cnt_up) current_state <= EOF;
					else current_state <= current_state;
				end
				EOF: begin
					if(byte_cnt_up) current_state <= IDLE;
					else current_state <= current_state;
				end
				default: current_state <= IDLE;
			endcase
			//save the inputing several byte
			if(Le) begin
				n_reg <= N;
				din_reg[0] <= {Din[0],Din[1],Din[2],Din[3],Din[4],Din[5],Din[6],Din[7]};
				if(N!=0)
					din_cnt_en <= 1;
				else 
					din_cnt_en <= din_cnt_en;
			end
			else if(din_cnt_en) begin
				din_reg[din_cnt] <= {Din[0],Din[1],Din[2],Din[3],Din[4],Din[5],Din[6],Din[7]};
				if(din_cnt == n_reg)
					din_cnt_en <= 0;
				else
					din_cnt_en <= din_cnt_en;
			end
			else begin
				n_reg <= n_reg;
				for(index=0; index<16; index=index+1) begin
					din_reg[index] <= din_reg[index];
				end
				din_cnt_en <= din_cnt_en;
			end
			//input Din numbers counter
			if(din_cnt_en)
				din_cnt <= din_cnt + 1;
			else
				din_cnt <= 1;
			//counters in the data flow
			//bit_counter
			if(pie_clk & bit_en & (!bit_cnt_up))
				bit_cnt <= bit_cnt + 1;
			else if(bit_cnt_up)
				bit_cnt <= 0;
			else if(!bit_en)
				bit_cnt <= 0;
			else
				bit_cnt <= bit_cnt;
			//byte_counter
			if(bit_cnt_up & bit_en & !(byte_cnt_up))
				byte_cnt <= byte_cnt + 1;
			else if(byte_cnt_up)
				byte_cnt <= 0;
			else if(!bit_en)
				byte_cnt <= 0;
			else
				byte_cnt <= byte_cnt;
			//frame_cnt
			if(byte_cnt_up & frame_en & (!frame_cnt_up))
				frame_cnt <= frame_cnt + 1;
			else if(frame_cnt_up)
				frame_cnt <= 0;
			else if(!bit_en)
				frame_cnt <= 0;
			else
				frame_cnt <= frame_cnt;
		end
	end
	
	assign bit_cnt_up = (bit_cnt == bit_cnt_full)&pie_clk;
	assign byte_cnt_up = (byte_cnt == byte_cnt_full)&bit_cnt_up;
	assign frame_cnt_up = (frame_cnt == n_reg)&byte_cnt_up;
	
	//Data Flow: counter management and output generate
	always @(*) begin
		bit_en = 0;
		frame_en = 0;
		bit_cnt_full = 1;
		byte_cnt_full = 7;
		Dout = 0;
		case(current_state)
			SOF: begin
				bit_en = 1;
				byte_cnt_full = 2;
				case(byte_cnt)
					0:	bit_cnt_full = 1;
					1:	bit_cnt_full = 1;
					2:	bit_cnt_full = 3;
				endcase
			end
			BYTE: begin
				bit_en = 1;
				frame_en = 1;
				byte_cnt_full = 7;
				if(din_reg[frame_cnt][byte_cnt])
					bit_cnt_full = 3;
				else
					bit_cnt_full = 1;
			end
			EOF: begin
				bit_en = 1;
				byte_cnt_full = 1;
				bit_cnt_full = 1;
			end
		endcase
		case(current_state)
			SOF: begin
				if((byte_cnt==1&&bit_cnt==0) || (byte_cnt==2&&bit_cnt!=3))
					Dout = 1;
			end
			BYTE: begin
				if(din_reg[frame_cnt][byte_cnt]) begin
					current_data = 1;
					if(bit_cnt!=3)
						Dout=1;
				end
				else begin
					current_data = 0;
					if(bit_cnt==0)
						Dout=1;
				end
			end
			EOF: begin
				if(byte_cnt==1&&bit_cnt==0)
					Dout=1;
			end
		endcase
	end
	

endmodule